﻿using System;
using System.Collections.Generic;
using System.IO;
using System.Net.Mail;
using System.Reflection;

namespace Mbox2Eml.Extensions
{
    /// <summary>
    /// Adding Save() functionality to Microsoft.Net.Mail.MailMessage
    /// By Allan Eagle | 11 Jan 2009 
    /// http://www.codeproject.com/Articles/32434/Adding-Save-functionality-to-Microsoft-Net-Mail-Ma
    /// </summary>
    public static class MailMessageExtensions
    {
        public static string X_DATE = "X-M2E-Date";
        public static string X_TO = "X-M2E-To";

        //public static object 

        public static void Save(this MailMessage mailMsg, string fileName)
        {
            var assembly = typeof(SmtpClient).Assembly;
            Type mailWriterType = assembly.GetType("System.Net.Mail.MailWriter");

            using (var fileStream = new MemoryStream())
            {
                // Get reflection info for MailWriter contructor
                var mailWriterConstructor = mailWriterType.GetConstructor(
                    BindingFlags.Instance | BindingFlags.NonPublic,
                    null,
                    new[] { typeof(Stream) },
                    null);

                // Construct MailWriter object with our FileStream
                object mailWriter = mailWriterConstructor.Invoke(new object[] { fileStream });

                // Get reflection info for Send() method on MailMessage
                var sendMethod = typeof(MailMessage).GetMethod("Send", BindingFlags.Instance | BindingFlags.NonPublic);
                var methodParameters = sendMethod.GetParameters(); // BaseWriter, sendEnvelope, allowUniCode

                // Call Send method in MailWriter : Send(mailWriter, true) or Send(mailWriter, true, true)
                var callParameters = methodParameters.Length == 2 ? new[] { mailWriter, true } : new[] { mailWriter, true, true };
                sendMethod.Invoke(mailMsg, BindingFlags.Instance | BindingFlags.NonPublic, null, callParameters, null);
                
                var replaceHeaders = new Dictionary<string, string> { { "Date", X_DATE }, { "To", X_TO } };

                // var skipHeaders = replaceHeaders.Values;

                using (TextWriter tw = new StreamWriter(fileName))
                {
                    fileStream.Position = 0;
                    using (var sr = new StreamReader(fileStream))
                    {
                        while (!sr.EndOfStream)
                        {
                            bool skipLine = false;
                            string line = sr.ReadLine();

                            if (line != null)
                            {
                                foreach (string header in replaceHeaders.Keys)
                                {
                                    string newHeader = replaceHeaders[header];
                                    string newValue = mailMsg.Headers[newHeader];

                                    if (line.StartsWith(header) && newValue != null)
                                    {
                                        line = header + ": " + newValue;
                                        break;
                                    }

                                    if (line.StartsWith(newHeader))
                                    {
                                        skipLine = true;
                                        break;
                                    }
                                }
                            }

                            if (!skipLine)
                            {
                                tw.WriteLine(line);
                            }
                        }
                    }
                }

                // Finally get reflection info for Close() method on our MailWriter
                //MethodInfo closeMethod = mailWriter.GetType().GetMethod("Close", BindingFlags.Instance | BindingFlags.NonPublic);

                // Call close method
                //closeMethod.Invoke(mailWriter, BindingFlags.Instance | BindingFlags.NonPublic, null, new object[] { }, null);
            }
        }
    }
}